#include "ScanLine.h"
#include <algorithm>
#include <iostream>
#include <gl/glut.h>

ScanLine::Edge::Edge() {}
ScanLine::Edge::Edge(int x0, int y0, int x1, int y1) {
	if (y0 > y1) {
		this->x0 = x1;
		this->y0 = y1;
		this->x1 = x0;
		this->y1 = y0;
	}
	else {
		this->x0 = x0;
		this->y0 = y0;
		this->x1 = x1;
		this->y1 = y1;
	}
}
bool ScanLine::Edge::operator < (const Edge &u) const {
	return y0 < u.y0;
}

ScanLine::ActiveEdge::ActiveEdge(){}
ScanLine::ActiveEdge::ActiveEdge(int ymax, int x, int dx, int dy)
	:ymax(ymax), x(x), dx(dx), dy(dy), c(0)
{}
bool ScanLine::ActiveEdge::operator < (const ActiveEdge &u) const {
	return x < u.x;
}
void ScanLine::ActiveEdge::xinc() {
	c += 2 * dx;
	while (c >= dy) {
		x++;
		c -= 2 * dy;
	}
	while (c <= -dy) {
		x--;
		c += 2 * dy;
	}
}

ScanLine::ScanLine(Polygon &polygon)
{
	int PointsLen = polygon.size();
	if (PointsLen < 2) return;
	for (int i = 0; i < PointsLen - 1; i++) {
		et.push_back(Edge(polygon[i].x, polygon[i].y, polygon[i + 1].x, polygon[i + 1].y));
	}
	et.push_back(Edge(polygon[0].x, polygon[0].y, polygon[PointsLen - 1].x, polygon[PointsLen - 1].y));
	sort(et.begin(), et.end());

	start = et[0].y0;
	end = et[0].y1;
	std::vector<Edge>::iterator eit = et.begin();
	for (; eit != et.end(); ++eit) {
		start = std::min(start, eit->y0);
		end = std::max(end, eit->y1);
	}
}

void ScanLine::draw()
{
	if (et.empty()) return;
	if (et.size() <= 2) {
		glBegin(GL_LINES);
		glVertex2i(et[0].x0, et[0].y0);
		glVertex2i(et[0].x1, et[0].y1);
		glEnd();
		return;
	}
	std::vector<Edge>::iterator eit = et.begin();
	for (int y = start; y <= end; y++) {
		for (; eit != et.end() && eit->y0 <= y; ++eit) {
			if (eit->y0 != eit->y1) {
				aet.push_back(ActiveEdge(eit->y1 - 1, eit->x0, eit->x1 - eit->x0, eit->y1 - eit->y0));
			}
		}
		std::sort(aet.begin(), aet.end());
		bool flag = false;
		int xstart;

		glBegin(GL_LINES);
		std::vector<ActiveEdge>::iterator aeit = aet.begin();
		while (aeit != aet.end()) {
			if (!flag) {
				flag = true;
				xstart = aeit->x;
			}
			else {
				flag = false;
				glVertex2i(xstart, y);
				glVertex2i(aeit->x, y);
			}
			if (y >= aeit->ymax) {
				aeit = aet.erase(aeit);
			}
			else {
				aeit->xinc();
				++aeit;
			}
		}
		glEnd();
	}
}
